//
//  File Name   :   FileSystemWin32.cpp
//  Description :   FileSystemWin32 implementation file.
//  Author      :   William McVicar
//  Mail        :   mcvicar.william@gmail.com
//

//  Precompiled Header
#include "CommonDefines.h"
#if defined (PROTEIN_PLATFORM_WINDOWS)
//  Library Includes

//  Local Includes
#include <TCHAR.h>
#include <direct.h>
#include <varargs.h>

//  This includes
#include "FileSystemWin32.h"

//  Macros

//  Static Variables

//  Static Function Prototypes

//  Implementation

namespace Protein
{

FileSystemWin32::FileSystemWin32()
{
	c8 buf[ PROTEIN_MAX_PATH ];
	memset( buf, 0, sizeof( buf ) );
	GetCurrentDirectoryA( sizeof( buf ), buf );

	Directory directory( buf );
	directory.AddSubDirectoriesRecursive();
	SetWorkingDirectory(directory);
}

FileSystemWin32::~FileSystemWin32()
{
	//
}

IFileSystem::eFILESYSTEM_ERROR FileSystemWin32::FileExists( const c8* _szFilename, char* _pBuffer, const Directory& _krDirectory, bool _bRecursive ) const
{
	WIN32_FIND_DATA FindFileData;

	std::wstringstream wstream;
	wstream << _krDirectory.GetPathCStr() << IFileSystem::sm_wszPathSeparator << _szFilename;
	c16 buf[ PROTEIN_MAX_PATH ];
	wcscpy_s( buf, wstream.str().c_str() );

	if( INVALID_HANDLE_VALUE != FindFirstFile( wstream.str().c_str(), &FindFileData ) )
	{
		strcpy( _pBuffer, _krDirectory.GetPathCStr() );
		strcat( _pBuffer, IFileSystem::sm_szPathSeparator );
		strcat( _pBuffer, _szFilename );
		return IFileSystem::FILESYSTEM_ERROR_OK;
	}

	if( !_bRecursive )
		return IFileSystem::FILESYSTEM_ERROR_FILE_NOT_FOUND; //dont check sub directories
	
	IFileSystem::eFILESYSTEM_ERROR error = IFileSystem::FILESYSTEM_ERROR_FILE_NOT_FOUND;
	for( u32 i = 0; i < _krDirectory.GetSubDirectories().size(); ++i )
	{
		error = FileExists( _szFilename, _pBuffer, _krDirectory.GetSubDirectories()[i] );
		if( error ==  IFileSystem::FILESYSTEM_ERROR_OK )
			return error;
	}

	return error;
}

bool FileSystemWin32::Open( const c8* _szFilename, const c8* _szFileMode, FILE** _ppFile, bool _bAbsolutePath )
{
	errno_t err = 0;

	if( !_bAbsolutePath )
	{
		c8 buf[ PROTEIN_MAX_PATH ];
		if( IFileSystem::FILESYSTEM_ERROR_OK != FileExists( _szFilename, buf ) )
			return false;

		err = fopen_s( _ppFile, buf, _szFileMode );
	}
	else
	{
		err = fopen_s( _ppFile, _szFilename, _szFileMode );
	}
	if( err != 0 )
	{
		if( *_ppFile )
			fclose( *_ppFile );
		return false;
	}

	return true;
}

bool FileSystemWin32::Close( FILE* _pFile )
{
	if( fclose( _pFile ) )
		return false;
	return true;
}

i32 FileSystemWin32::Read( u8* _pBuffer, u32 _uiSize, u32 _uiCount, FILE* _pFile )
{
	return fread( _pBuffer, _uiSize, _uiCount, _pFile );
}

i32 FileSystemWin32::Write( u8* _pBuffer, u32 _uiSize, u32 _uiCount, FILE* _pFile )
{
	return fwrite( _pBuffer, _uiSize, _uiCount, _pFile );
}

bool FileSystemWin32::Seek( FILE* _pFile, u32 _uiOffset, eSEEK_ORIGIN _eOrigin )
{
	return ( fseek( _pFile, _uiOffset, i32(_eOrigin) ) == 0 );
}

void FileSystemWin32::Rewind( FILE* _pFile )
{
	rewind( _pFile );
}

u32 FileSystemWin32::GetOffset( FILE* _pFile )
{
	return ftell( _pFile );
}

int vfscanf(FILE* file, const char *format, va_list argPtr)
{
	// http://www.codeguru....1/?thread=61724
	// Get an upper bound for the # of args
	size_t count = 0;
	const char* p = format;
	bool b = true;
	while(b)
	{
		char c = *(p++);
		if (c == 0)
 			break;
			
		if (c == '%' && (p[0] != '*' && p[0] != '%'))
			++count;
	}
	if (count <= 0)
		return 0;
	int result;
	// copy stack pointer
	_asm
	{
		mov esi, esp;
	}
	// push variable parameters pointers on stack
	for (int i = count - 1; i >= 0; --i)
	{
		_asm
		{
			mov eax, dword ptr[i];
			mov ecx, dword ptr [argPtr];
			mov edx, dword ptr [ecx+eax*4];
			push edx;
		}
	}
	int stackAdvance = (2 + count) * 4;
	_asm
	{
		// now push on the fixed params
		mov eax, dword ptr [format];
		push eax;
		mov eax, dword ptr [file];
		push eax;
		// call fscanf, and more the result in to result
		call dword ptr [fscanf];
		mov result, eax;
		// restore stack pointer
		mov eax, dword ptr[stackAdvance];
		add esp, eax;
		//mov esp, esi;
	}
	return result;
}


i32 FileSystemWin32::ReadFormattedString( FILE* _pFile, const c8* _szFormat, ... )
{
	va_list args;
	va_start( args, _szFormat );
	return vfscanf( _pFile, _szFormat, args );
}

i32 FileSystemWin32::WriteFormattedString( FILE* _pFile, const c8* _szFormat, ... )
{
	va_list args;
	va_start( args, _szFormat );
	return vfprintf_s( _pFile, _szFormat, args );
}

bool FileSystemWin32::ReadLine( c8* _pBuffer, FILE* _pFile )
{
	if( fgets( _pBuffer, PROTEIN_MAX_PATH, _pFile ) )
		return true;

	return false;
}

bool FileSystemWin32::ReadChunk( c8* _pBuffer, FILE* _pFile, i32 _iNum )
{
	if( !fgets( _pBuffer, _iNum, _pFile ) )
		return false;

	i32 iLength = strlen( _pBuffer ) + 1;
	if( iLength >= _iNum )
		return true;

	i32 iRemaining = _iNum - iLength + 1;
	c8* buf = reinterpret_cast< c8* >( ::malloc( iRemaining * sizeof( c8 ) ) );
	while( iLength < _iNum )
	{
		if( !fgets( buf, iRemaining, _pFile ) )
		{
			::free( reinterpret_cast< void* >( buf ) );
			return false;
		}

		strcat_s( _pBuffer, strlen( buf ), buf );
		iLength = strlen( _pBuffer ) + 1; 
	}

	::free( reinterpret_cast< void* >( buf ) );
	return true;
}

bool FileSystemWin32::WriteString( const c8* _szBuffer, FILE* _pFile )
{
	if( fputs( _szBuffer, _pFile ) != EOF )
		return true;

	return false;
}

i32 FileSystemWin32::GetFileSize( FILE* _pFile )
{
	if( !_pFile )
		return -1;	//file isnt open

	rewind( _pFile );
	fseek( _pFile, 0, SEEKEND );

	i32 size = ftell( _pFile );
	rewind( _pFile );

	return size;
}

}

#endif //PROTEIN_PLATFORM_WINDOWS